/*
 * @Author: zzd993
 * @Date: 2022-09-20 14:08:45
 * @LastEditors: zzd993
 * @LastEditTime: 2022-11-21 13:45:04
 * @FilePath: \vue设计与实现\testTS.ts
 * Copyright (c) 2022 by ZZD, All Rights Reserved. 
 */
export {};
// 1.
// 告诉ts确实有个$的变量
// declare let $: any;

// 2. 
// 静态属性static
class something {
  static instances = 0;
  constructor() {
    something.instances++;
  }
}
let s1 = new something();
let s2 = new something();
// console.log(something.instances);

// 3.
/**
 * 访问修饰符
 * public：类、子类、实例都可访问，默认值。
 * protected：类、子类能，实例不能
 * private：类能、子类、实例不能
 */

// 4.
/**
 * 抽象类abstract
 * 可以作用在类及类的任何成员上。
 * 拥有abstract修饰符意味着该函数不能直接被调用，不能被实例化，要被继承，并且子类必须实现这个功能
 */

// 5.
// 普遍的写法
// class Foo {
//     x: number
//     constructor(x:number) {
//         this.x = x
//     }
// }
// 简写,会在类上自动声明，并且从构造器中复制过去
class Foo {
  constructor(public x: number) {}
}

// 6.
/**
 * 基本语法
 */
// 原始类型
let num: number, str: string, bool: boolean;
// 数组
let boolArr: boolean[];
boolArr = [true, false];
// 接口
interface Name {
  first: string;
  second: string;
}
let name: Name = {
  first: "z",
  second: "zd",
};
// 特殊类型 any、null、undefined、void
// any、null和undefined可以被赋值给所有类型，要开启strictNullChecks选项
let a: number;
a = null;
a = undefined;
function log(): void {}
// 泛型: 对参数和返回值的约束
function reverse<T>(items: T[]): T[] {
  const toreturn = [];
  for (let i = items.length - 1; i >= 0; i--) {
    toreturn.push(items[i]);
  }
  return toreturn;
}
let reversed = reverse([1, 2, 3]);
// console.log(reversed);

// 联合类型
function formatCommandline(command: string[] | string) {
  let line = "";
  if (typeof command === "string") {
    line = command.trim();
  } else {
    line = command.join(" ").trim();
  }
}
// 交叉类型
// T、U继承自对象，返回值有T和U的全部属性
function extend<T extends object, U extends object>(
  first: T,
  second: U
): T & U {
  const res = <T & U>{};
  for (let id in first) {
    (<T>res)[id] = first[id];
  }
  for (let id in second) {
    if (!res.hasOwnProperty(id)) {
      (<U>res)[id] = second[id];
    }
  }
  return res;
}
let x = extend({ a: "hello" }, { b: 42 });
// console.log(x);

// 元组类型
let nameNumber: [string, number] = ["1", 2];
let [name2, num2] = nameNumber;

// 类型别名
// 与接口不同，他可以为任意的类型注解提供类型别名
type StrOrNum = string | number;

/**
 * 接口
 */

// 类实现接口：implements
interface Point {
  x: number;
  y: number;
}
class MyPoint implements Point {
  x: number;
  y: number;
}

/**
 * 枚举
 */
enum CardSuit {
  Clubs,
  Diamonds,
  Hearts,
  Spaders,
}
// console.log(CardSuit.Clubs); // 0
enum strEnum {
  a = "1",
  b = "2",
}
// console.log(strEnum.a);

interface Overloaded {
  (foo: string): string;
  (foo: number): number;
}

// 实现接口的一个例子：
function stringOrNumber(foo: number): number;
function stringOrNumber(foo: string): string;
function stringOrNumber(foo: any): any {
  if (typeof foo === "number") {
    return foo * foo;
  } else if (typeof foo === "string") {
    return `hello ${foo}`;
  }
}

const overloaded: Overloaded = stringOrNumber;

// 使用
const str2 = overloaded(""); // str 被推断为 'string'
const num3 = overloaded(123); // num 被推断为 'number'

/**
 * 类型断言 允许你覆盖ts的推断，并且能以你任何你想要的方式分析它，这种机制被称为「类型断言」。
 */

//  const foo = {};
//  foo.bar = 123; // Error: 'bar' 属性不存在于 ‘{}’
//  foo.bas = 'hello'; // Error: 'bas' 属性不存在于 '{}'
interface Foo {
  bar: number;
  bas: string;
}

const foo = {} as Foo;
foo.bar = 123;
foo.bas = "hello";
// 双重断言
//   function handler(event: Event) {
//     const element = event as HTMLElement; // Error: 'Event' 和 'HTMLElement' 中的任何一个都不能赋值给另外一个
//   }
function handler(event: Event) {
  const element = event as any as HTMLElement; // ok
}

// 泛型  常用T、U、V表示
class Queue<T> {
  private data: T[] = [];
  push = (item: T) => this.data.push(item);
  pop = (): T | undefined => this.data.shift();
}
const queue = new Queue<number>();

interface Point2D {
  x: number;
  y: number;
}

interface Point3D {
  x: number;
  y: number;
  z: number;
}

const point2D: Point2D = { x: 0, y: 10 };
const point3D: Point3D = { x: 0, y: 10, z: 20 };
function iTakePoint2D(point: Point2D) {
  /* do something */
  console.log(point);
}

iTakePoint2D(point2D); // ok, 完全匹配
iTakePoint2D(point3D); // 额外的信息，没关系    类型是结构化的

const iTakeSomethingAndPassItAnErr = (x: (err: Error, data: any) => void) => {
  /* 做一些其他的 */
};

iTakeSomethingAndPassItAnErr(() => null); // ok
iTakeSomethingAndPassItAnErr((err) => null); // ok
iTakeSomethingAndPassItAnErr((err, data) => null); // ok


interface Bar {
  [key: string]: number;
  x: number;
  y: number
}

let value: unknown;
value = 1;
value = 's';






/**
 * 泛型
 */

function pickObjectKeys(obj, keys) {
  let res = {}
  for (const key of keys) {
    if(key in obj) {
      res[key] = obj[key]
    }
  }
  return res
}
const language = {
  name: 'ts',
  age: 8,
  extensions: ['ts', 'tsx']
}
const ageExtensions = pickObjectKeys2(language, ['age','extensions'])
// console.log(ageExtensions);

function pickObjectKeys2<T, K extends keyof T>(obj: T, keys: K[]) {
  let res = {} as Pick<T, K>
  for (const key of keys) {
    if(key in obj) {
      res[key] = obj[key]
    }
  }
  return res
}


function identity<T>(value: T): T {
  return value
}

const res = identity(123)

type ProgrammingLanguage = {
  name: string
}

const res2 = identity<ProgrammingLanguage>({name: 'ts'})
// console.log(res2)

// async function fetchApi<ResType = Record<string, any>>(path:string): Promise<ResType> {
//   const response = await fetch(`http://example.com/api${path}`)
//   return response.json()
// }

type User = {
  name: string
}

// const data = await fetchApi('./users')
// console.log(data.a);

function stringObjectKeyValues<T extends Record<string, any>>(obj: T) {
  return Object.keys(obj).reduce((acc, key) => ({
    ...acc,
    [key]: JSON.stringify(obj[key])
  }), {} as { [K in keyof T]: string})
}

const stringfiedValues = stringObjectKeyValues({ a: '1', b: 2, c: true, d:[1,2,3]})

// console.log(stringfiedValues);


interface MyInterface<T> {
  field: T
}

class Myclass<T> {
  field: T
  constructor(field: T) {
    this.field = field
  }
}


type BooleanFields<T> = {
  [K in keyof T]: boolean
}
// 摸鱼一天

// 摸鱼三天

// 摸鱼四天？

// 重整旗鼓

// 遍历K，k的每个属性的类型都是T
// type Record<K extends keyof any, T> = {
//   [P in K]: T
// }

type record = 'one' | 'two' | 'three'
const numArr: Record<record, number> = {
  one: 1,
  two: 2,
  three: 3,
}
// console.log(numArr);

interface Foo {
  name: string
  age: number
}
type Bar2 = Partial<Foo>

// 相当于
// type Bar2 = {
//   name?: string
//   age?: number
// }

type Bar3 = Required<Foo>
// 相当于
// type Bar = {
//   name: string
//   age: number
// }

type A = number | string | boolean
type B = number | boolean

type Fo2o = Exclude<A, B>